﻿/* All V8.NET source is governed by the LGPL licensing model. Please keep these comments intact, thanks.
 * Developer: James Wilkins (jameswilkins.net).
 * Source, Documentation, and Support: https://v8dotnet.codeplex.com
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Web;

namespace V8.Net
{
    // ========================================================================================================================
    // (.NET and Mono Marshalling: http://www.mono-project.com/Interop_with_Native_Libraries)
    // (Mono portable code: http://www.mono-project.com/Guidelines:Application_Portability)
    // (Cross platform p/invoke: http://www.gordonml.co.uk/software-development/mono-net-cross-platform-dynamic-pinvoke/)

    /// <summary>
    /// 创建一个新的托管V8Engine包装实例，并将其与新的本机V8引擎关联。
    /// 引擎未实现锁定，因此要使其线程安全，您应针对引擎实例进行锁定（即lock（myEngine）{...}）。
    /// </summary>
    public unsafe partial class V8Engine
    {
        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// 设置为1970年1月1日作为固定日期。将DateTime值转换为JavaScript Date对象时使用。
        /// </summary>
        public static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);

        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// 创建的所有V8引擎的静态数组。
        /// </summary>
        public V8Engine[] Engines { get { return _Engines; } }
        internal static V8Engine[] _Engines = new V8Engine[10];

        void _RegisterEngine(int engineID) //('engineID'是托管端引擎ID,从0开始)
        {
            lock (_Engines)
            {
                if (engineID >= _Engines.Length)
                {
                    Array.Resize(ref _Engines, (5 + engineID) * 2); //（如果分配了许多引擎，无论出于何种疯狂原因，'* 2'都会使容量指数增加）
                }
                _Engines[engineID] = this;
            }
        }

        // --------------------------------------------------------------------------------------------------------------------

        internal readonly NativeV8EngineProxy* _NativeV8EngineProxy;
        /// <summary>（需要对此进行引用，否则反向p/调用将失败）</summary>
        V8GarbageCollectionRequestCallback ___V8GarbageCollectionRequestCallback;
        static readonly object _GlobalLock = new object();

        ObjectTemplate _GlobalObjectTemplateProxy;

        public ObjectHandle GlobalObject { get; private set; }

#if !(V1_1 || V2 || V3 || V3_5)
        public dynamic DynamicGlobalObject { get { return GlobalObject; } }
#endif

        // --------------------------------------------------------------------------------------------------------------------

        static Assembly Resolver(object sender, ResolveEventArgs args)
        {
            if (args.Name.StartsWith("V8.Net.Proxy.Interface"))
            {
                string assemblyRoot = "";

                if (HttpContext.Current != null)
                    assemblyRoot = HttpContext.Current.Server.MapPath("~/bin");
                else
                    assemblyRoot = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

                //// ... validate access to the root folder ...
                //var permission = new FileIOPermission(FileIOPermissionAccess.Read, assemblyRoot);
                //var permissionSet = new PermissionSet(PermissionState.None);
                //permissionSet.AddPermission(permission);
                //if (!permissionSet.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet))

                // ... get platform details ...

                var bitStr = IntPtr.Size == 8 ? "x64" : "x86";
                var platformLibraryPath = Path.Combine(assemblyRoot, bitStr);
                var fileName = Path.Combine(platformLibraryPath, "V8.Net.Proxy.Interface." + bitStr + ".dll");

                // ... attempt to update environment variable automatically for the native DLLs ...
                // (see: http://stackoverflow.com/questions/7996263/how-do-i-get-iis-to-load-a-native-dll-referenced-by-my-wcf-service
                //   and http://stackoverflow.com/questions/344608/unmanaged-dlls-fail-to-load-on-asp-net-server)

                try
                {
                    var path = System.Environment.GetEnvironmentVariable("PATH"); // TODO: Detect other systems if necessary.
                    System.Environment.SetEnvironmentVariable("PATH", path + ";" + platformLibraryPath);
                }
                catch { }

                AppDomain.CurrentDomain.AssemblyResolve -= Resolver;  // (handler is only needed once)

                // ... if the current directory has an x86 or x64 folder for the bit depth, automatically change to that folder ...
                // (this is required to load the correct VC++ libraries if made available locally)

                var bitLibFolder = Path.Combine(Directory.GetCurrentDirectory(), bitStr);
                if (Directory.Exists(bitLibFolder))
                    Directory.SetCurrentDirectory(bitLibFolder);

                try
                {
                    return Assembly.LoadFile(fileName);
                }
                catch (Exception ex)
                {
                    var msg = "加载失败'" + fileName + "'.V8.NET在'" + bitStr + "'模式下运行.  一些要检查的地方: \r\n"
                        + "1. 包含VC ++ 2012可再发行库，但是如果由于某些原因而缺少，请从Microsoft网站下载并安装。\r\n"
                        + "2. 您是否从ZIP文件下载了DLL？ 如果在Windows上这样做，则必须先解压缩该zip文件的文件属性，然后在解压缩文件之前对其进行“取消阻止”操作。\r\n"
                        ;
                    if (HttpContext.Current != null)
                        msg += "3. 确保路径'" + assemblyRoot + "'可用于应用程序池标识（通常为'IIS_IUSRS'或类似的用户/组读取和执行)";
                    else
                        msg += "3. 确保应用程序可以访问路径'" + assemblyRoot + "'来加载所需的库.";
                    throw new InvalidOperationException(msg + "\r\n", ex);
                }
            }

            return null;
        }

        static V8Engine()
        {
            AppDomain.CurrentDomain.AssemblyResolve += Resolver;
        }

        public V8Engine()
        {
            lock (_GlobalLock) //（这是必需的，因为引擎代理实例ID是在静态'_DisposedEngines'向量中[在本机端进行跟踪的（用于快速处理句柄）））
            {
                _NativeV8EngineProxy = V8NetProxy.CreateV8EngineProxy(false, null, 0);

                _RegisterEngine(_NativeV8EngineProxy->ID);

                WithIsolateScope = () =>
                {
                    _GlobalObjectTemplateProxy = CreateObjectTemplate<ObjectTemplate>();
                    _GlobalObjectTemplateProxy.UnregisterPropertyInterceptors(); // (it's much faster to use a native object for the global scope)
                    GlobalObject = V8NetProxy.SetGlobalObjectTemplate(_NativeV8EngineProxy, _GlobalObjectTemplateProxy._NativeObjectTemplateProxy); // (returns the global object handle)
                };
            }

            ___V8GarbageCollectionRequestCallback = _V8GarbageCollectionRequestCallback;
            V8NetProxy.RegisterGCCallback(_NativeV8EngineProxy, ___V8GarbageCollectionRequestCallback);

            _Initialize_Handles();
            _Initialize_ObjectTemplate();
            _Initialize_Worker(); //（最后一次！！-希望一切准备就绪）
        }

        ~V8Engine()
        {
            PauseWorker(); //（仅在成功暂停后返回）

            if (_NativeV8EngineProxy != null)
                V8NetProxy.DestroyV8EngineProxy(_NativeV8EngineProxy);
        }

        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// 调用此方法将在本机包装器中强制执行“空闲”循环，直到V8引擎完成待处理的工作任务为止。
        /// 所做的工作有助于减少V8内的内存占用。
        /// <para>(See also: <seealso cref="DoIdleNotification()"/>)</para>
        /// </summary>
        public void ForceV8GarbageCollection()
        {
            V8NetProxy.ForceGC(_NativeV8EngineProxy);
        }

        /// <summary>
        /// 调用此方法将通知本机V8引擎在返回之前最多执行1000个待处理的工作任务（这是V8中的默认设置）。
        /// 所做的工作有助于减少V8内的内存占用。 这有助于垃圾收集器知道何时开始收集不再使用的对象和值。
        /// 如果还有更多待处理的工作，则此方法返回true。
        /// <para>(See also: <seealso cref="ForceV8GarbageCollection()"/>)</para>
        /// </summary>
        /// <param name="hint">向本地V8引擎提示在返回之前可以执行多少工作（V8的默认值为1000个工作任务）。</param>
        /// <returns>如果还有更多工作要做，则为True。</returns>
        public bool DoIdleNotification(int hint = 1000)
        {
            return V8NetProxy.DoIdleNotification(_NativeV8EngineProxy, hint);
        }

        bool _V8GarbageCollectionRequestCallback(HandleProxy* persistedObjectHandle)
        {
            if (persistedObjectHandle->_ObjectID >= 0)
            {
                lock (_Objects)
                {
                    var weakRef = _Objects[persistedObjectHandle->_ObjectID];
                    if (weakRef != null)
                        return weakRef.Object._OnNativeGCRequested(); //（通知对象请求了V8 GC）
                }
            }
            return true; //（托管句柄不存在，因此请继续处理本机句柄[代理句柄]）
        }

        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// 在基本Handle范围内执行给定的lambda表达式（用于与非JavaScript环境相关的操作，例如仅调用“ V8Engine.Create ???”值方法之一）。
        /// 尽管此范围对于基本的句柄操作（例如仅创建值）非常合适，但调用更改此范围的引擎方法比上下文范围要快一些，
        /// 因为不需要在堆栈上创建上下文和隔离范围对象。 此范围不是线程安全的（因为需要隔离）。
        /// </summary>
        public Action WithHandleScope { set { if (value != null) V8NetProxy.WithV8HandleScope(_NativeV8EngineProxy, value); } }

        /// <summary>
        /// 在隔离范围内执行给定的lambda表达式。 这意味着线程安全锁作用域和句柄作用域（但没有上下文）。
        /// V8引擎要求JavaScript代码在Isolate范围内运行，以便支持多个线程（与锁定范围一起使用时）。
        /// 如有疑问，请改用“ WithContextScope”，其中包括这一点。
        /// </summary>
        public Action WithIsolateScope { set { if (value != null) V8NetProxy.WithV8IsolateScope(_NativeV8EngineProxy, value); } }

        /// <summary>
        /// 在Context范围(执行环境)内执行给定的lambda表达式.这也意味着隔离和处理范围（因此这也是线程安全的）。
        /// <para>V8引擎要求JavaScript代码在Context范围内运行，该范围包含执行环境(全局对象所在的位置)</para>
        /// <para>这是执行脚本所必需的.如果您不执行脚本或更改执行环境,则使用'WithIsolateScope'可能会快一些。</para>
        /// </summary>
        public Action WithContextScope { set { if (value != null) V8NetProxy.WithV8ContextScope(_NativeV8EngineProxy, value); } }

        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// 在V8引擎上执行JavaScript并返回结果
        /// </summary>
        /// <param name="script">要运行的脚本</param>
        /// <param name="sourceName">标识脚本源的字符串(方便调试)</param>
        /// <param name="throwExceptionOnError">如果为true,并且返回值表示错误,则抛出异常(默认为'false')</param>
        public Handle Execute(string script, string sourceName = "V8.NET", bool throwExceptionOnError = false)
        {
            Handle result = V8NetProxy.V8Execute(_NativeV8EngineProxy, script, sourceName);
            //(注意:执行整个脚本时速度不是问题,因此结果将在句柄对象中返回,而不是[safe]值)
            if (throwExceptionOnError)
                result.ThrowOnError();
            return result;
        }

        /// <summary>
        /// 在V8引擎上执行JavaScript，并将结果自动写入控制台（仅对支持“控制台”方法的应用程序有效）。
        /// <para>注意：这只是调用'Execute()'后跟'Console.WriteLine()'的快捷方式.</para>
        /// </summary>
        /// <param name="script">要运行的脚本。</param>
        /// <param name="sourceName">一个字符串，用于标识脚本的来源（方便调试）。</param>
        /// <param name="throwExceptionOnError">如果为true，并且返回值表示错误，则抛出异常（默认为'false'）。</param>
        public void ConsoleExecute(string script, string sourceName = "V8.NET", bool throwExceptionOnError = false)
        {
            Console.WriteLine(Execute(script, sourceName, throwExceptionOnError).AsString);
        }

        /// <summary>
        /// 在V8引擎上执行JavaScript，并将结果自动写入控制台（仅对支持“控制台”方法的应用程序有效）。
        /// 该脚本在执行前先输出到控制台窗口。
        /// <para>注意：这只是调用'Console.WriteLine(script)',然后调用'ConsoleExecute()'的快捷方式。</para>
        /// </summary>
        /// <param name="script">要运行的脚本。</param>
        /// <param name="sourceName">一个字符串，用于标识脚本的来源（方便调试）。</param>
        /// <param name="throwExceptionOnError">如果为true，并且返回值表示错误，则抛出异常（默认为'false'）。</param>
        public void VerboseConsoleExecute(string script, string sourceName = "V8.NET", bool throwExceptionOnError = false)
        {
            Console.WriteLine(script);
            Console.WriteLine(Execute(script, sourceName, throwExceptionOnError).AsString);
        }

        /// <summary>
        /// 从当前工作目录（或指定的绝对路径）加载JavaScript文件，并在V8引擎中执行该文件，然后返回结果。
        /// </summary>
        /// <param name="scriptFile">要加载的脚本文件。</param>
        /// <param name="sourceName">一个字符串，用于标识脚本的来源（方便调试）。</param>
        /// <param name="throwExceptionOnError">如果为true，则返回值表示错误，或者文件加载失败，将引发异常（默认为'false'）。</param>
        public Handle LoadScript(string scriptFile, string sourceName = "V8.NET", bool throwExceptionOnError = false)
        {
            Handle result;
            try
            {
                var jsText = File.ReadAllText(scriptFile);
                result = Execute(jsText, sourceName, throwExceptionOnError);
                if (throwExceptionOnError)
                    result.ThrowOnError();
            }
            catch (Exception ex)
            {
                if (throwExceptionOnError)
                    throw ex;
                result = CreateValue(Exceptions.GetFullErrorMessage(ex));
                result._Handle._HandleProxy->_ValueType = JSValueType.InternalError; //（需要标记已发生错误）
            }
            return result;
        }

        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// 创建一个新的本地V8 ObjectTemplate并将其与新的托管ObjectTemplate关联。
        /// <para>必须使用对象模板才能生成带有属性拦截器的对象（即，所有属性访问都重定向到托管端）。</para>
        /// </summary>
        /// <param name="registerPropertyInterceptors">如果为true（默认），则将使用属性拦截器（回调）来支持'IV8ManagedObject'对象。
        /// <para>注意：将其设置为false可以极大地提高性能，因为所有属性都只会存储在本机端（但是'IV8ManagedObject'
        ///此模板创建的对象将不会拦截属性访问。</para></param>
        /// <typeparam name="T">通常，除非您对其进行派生，否则始终为'ObjectTemplate'</typeparam>
        public T CreateObjectTemplate<T>(bool registerPropertyInterceptors = true) where T : ObjectTemplate, new()
        {
            var template = new T();
            template._Initialize(this, registerPropertyInterceptors);
            return template;
        }

        /// <summary>
        /// 创建一个新的本地V8 ObjectTemplate并将其与新的托管ObjectTemplate关联。
        /// <para>必须使用对象模板才能生成带有属性拦截器的对象(即，所有属性访问都重定向到托管端)</para>
        /// </summary>
        public ObjectTemplate CreateObjectTemplate() { return CreateObjectTemplate<ObjectTemplate>(); }

        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// 创建一个新的本地V8 FunctionTemplate并将其与新的托管FunctionTemplate关联
        /// <para>功能模板是必需的，以便将受管委托与V8中的JavaScript函数相关联</para>
        /// </summary>
        /// <typeparam name="T">通常，除非您对其进行派生，否则始终为“ FunctionTemplate”</typeparam>
        /// <param name="className">V8中的'类名'是在对象上使用'valueOf()'时返回的类型名。 如果为空，则假定为'V8Function'</param>
        /// <param name="callbackSource">函数执行时要调用的委托</param>
        public T CreateFunctionTemplate<T>(string className) where T : FunctionTemplate, new()
        {
            var template = new T();
            template._Initialize(this, className ?? typeof(V8Function).Name);
            return template;
        }

        /// <summary>
        /// 创建一个新的本地V8 FunctionTemplate并将其与新的托管FunctionTemplate关联
        /// <para>功能模板是必需的，以便将受管委托与V8中的JavaScript函数相关联</para>
        /// </summary>
        /// <param name="className">V8中的'类名'是在对象上使用'valueOf()'时返回的类型名。 如果为空(默认),则假定为'V8Function'</param>
        /// <param name="callbackSource">函数执行时要调用的委托</param>
        public FunctionTemplate CreateFunctionTemplate(string className = null) { return CreateFunctionTemplate<FunctionTemplate>(className); }

        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// 调用本地V8代理库以创建值实例，以在V8 JavaScript环境中使用。
        ///可以在此方法中使用'WithHandleScope'。
        /// </summary>
        public InternalHandle CreateValue(bool b) { return V8NetProxy.CreateBoolean(_NativeV8EngineProxy, b); }

        /// <summary>
        /// 调用本地V8代理库以创建32位整数，以在V8 JavaScript环境中使用。
        /// </summary>
        public InternalHandle CreateValue(Int32 num) { return V8NetProxy.CreateInteger(_NativeV8EngineProxy, num); }

        /// <summary>
        /// 调用本地V8代理库以创建一个64位数字(双精度),以在V8 JavaScript环境中使用
        /// </summary>
        public InternalHandle CreateValue(double num) { return V8NetProxy.CreateNumber(_NativeV8EngineProxy, num); }

        /// <summary>
        /// 调用本地V8代理库以创建在V8 JavaScript环境中使用的字符串
        /// </summary>
        public InternalHandle CreateValue(string str) { return V8NetProxy.CreateString(_NativeV8EngineProxy, str); }

        /// <summary>
        /// 调用本地V8代理库以创建错误字符串，以在V8 JavaScript环境中使用
        /// <para>注意：错误标志仅存在于关联的代理对象中。 如果将句柄传递给另一个操作，则只会传递字符串消息。</ para>
        /// </summary>
        public InternalHandle CreateError(string message, JSValueType errorType)
        {
            if (errorType >= 0) throw new InvalidOperationException("无效的错误类型.");
            return V8NetProxy.CreateError(_NativeV8EngineProxy, message, errorType);
        }

        /// <summary>
        /// 调用本地V8代理库以创建日期，以在V8 JavaScript环境中使用。
        /// </summary>
        /// <param name =“ ms”>自纪元（1970年1月1日）以来的毫秒数。 此值与JavaScript中的'SomeDate.getTime()'相同。</param>
        public InternalHandle CreateValue(TimeSpan ms) { return V8NetProxy.CreateDate(_NativeV8EngineProxy, ms.TotalMilliseconds); }

        /// <summary>
        /// 调用本地V8代理库以创建日期，以在V8 JavaScript环境中使用。
        /// </summary>
        public InternalHandle CreateValue(DateTime date) { return CreateValue(date.ToUniversalTime().Subtract(Epoch)); }

        /// <summary>
        /// 用托管对象包装给定的对象句柄，并有选择地将其与模板实例相关联。
        /// <para>注意：与给定句柄关联的任何其他托管对象都将导致错误。
        ///您应该选中'{Handle} .HasManagedObject'，或使用'GetObject()'方法来确保托管对象不存在。</para>
        /// <para>此方法存在以支持以下情况：1. V8上下文自动生成全局对象，并且
        /// 2. V8函数对象不是从模板生成的，但仍需要托管包装器。</ para>
        /// <para>注意：</para>
        /// </summary>
        /// <typeparam name="T">要创建的包装器类型(例如V8ManagedObject)</typeparam>
        /// <param name="v8Object">原始V8对象的句柄。</param>
        /// <param name="initialize">如果为true(默认值),则在返回之前在创建的包装器上调用'IV8NativeObject.Initialize()'。</param>
        internal T _CreateObject<T>(ITemplate template, InternalHandle v8Object, bool initialize = true, bool connectNativeObject = true)
            where T : V8NativeObject, new()
        {
            try
            {
                if (!v8Object.IsObjectType)
                    throw new InvalidOperationException("对象句柄类型是必需的(例如JavaScript对象或函数句柄).");

                // ...创建新的托管JavaScript对象，将其存储（以获取“ ID”），然后将其连接到本地V8对象
                var obj = _CreateManagedObject<T>(template, v8Object.PassOn(), connectNativeObject);

                if (initialize)
                    obj.Initialize();

                return obj;
            }
            finally
            {
                v8Object._DisposeIfFirst();
            }
        }

        /// <summary>
        /// 用托管对象包装给定的对象句柄。
        /// <para>注意：与给定句柄关联的任何其他托管对象都将导致错误。
        ///您应该选中'{Handle} .HasManagedObject'，或使用'GetObject()'方法来确保托管对象不存在。</para>
        /// <para>此方法存在以支持以下情况：1. V8上下文自动生成全局对象，并且
        /// 2. V8函数对象不是从模板生成的，但仍需要托管包装器。</para>
        /// <para>注意：</para>
        /// </ summary>
        /// <typeparam name="T">要创建的包装器类型(例如V8ManagedObject)</typeparam>
        /// <param name="v8Object">原始V8对象的句柄。</param>
        /// <param name="initialize">如果为true(默认值),则在返回之前在创建的包装器上调用'IV8NativeObject.Initialize()'</param>
        public T CreateObject<T>(InternalHandle v8Object, bool initialize = true)
            where T : V8NativeObject, new()
        {
            return _CreateObject<T>(null, v8Object, initialize);
        }

        /// <summary>
        /// See <see cref="CreateObject&lt;T>"/>.
        /// </summary>
        public V8NativeObject CreateObject(InternalHandle v8Object, bool initialize = true) { return CreateObject<V8NativeObject>(v8Object, initialize); }

        /// <summary>
        /// 创建一个新的CLR对象，该对象将由新的V8本机对象跟踪。
        /// </summary>
        /// <param name="initialize">如果为true(默认值),则在返回之前在创建的包装器上调用'IV8NativeObject.Initialize()</param>
        /// <typeparam name="T">自定义'V8NativeObject'类型，或仅使用'V8NativeObject'作为默认值。</typeparam>
        public T CreateObject<T>(bool initialize = true)
            where T : V8NativeObject, new()
        {
            //创建新的托管JavaScript对象并将其存储(以获取'ID')
            var obj = _CreateManagedObject<T>(null, null);

            try
            {
                //创建一个新的本机对象并将其与新的托管对象ID相关联
                obj._Handle._Set(V8NetProxy.CreateObject(_NativeV8EngineProxy, obj.ID));

                /* V8对象将具有一个关联的内部字段，该字段设置为上面创建的管理对象的索引，以便快速查找。 使用该索引
                 * 在发生回调时定位关联的被管理对象。 查找是使用自定义“ IndexedObjectList”管理器的快速O(1)操作。
                 */
            }
            catch (Exception ex)
            {
                //出了点问题，因此删除新的托管对象
                _RemoveObjectWeakReference(obj.ID);
                throw ex;
            }

            if (initialize)
                obj.Initialize();

            return (T)obj;
        }

        /// <summary>
        /// 仅创建一个新的本地V8对象。
        /// </summary>
        /// <param name="objectID">您可以将任意负数与对象关联以用于跟踪。 该数字必须小于或等于-2。
        /// 大于或等于0的值用于V8NativeObject实例的内部跟踪。
        /// -1是在创建新对象时自动设置的默认值（这仅表示“未设置ID”）。</param>
        public InternalHandle CreateObject(Int32 objectID = -2)
        {
            if (objectID > -2) throw new InvalidOperationException("Object IDs must be <= -2.");
            return V8NetProxy.CreateObject(_NativeV8EngineProxy, objectID);
        }

        /// <summary>
        /// 调用本地V8代理库以创建一个JavaScript数组，以在V8 JavaScript环境中使用
        /// </summary>
        public InternalHandle CreateArray(params InternalHandle[] items)
        {
            HandleProxy** nativeArrayMem = items.Length > 0 ? Utilities.MakeHandleProxyArray(items) : null;

            InternalHandle handle = V8NetProxy.CreateArray(_NativeV8EngineProxy, nativeArrayMem, items.Length);

            Utilities.FreeNativeMemory((IntPtr)nativeArrayMem);

            return handle;
        }

        /// <summary>
        /// 将值的枚举（通常来自集合，列表或数组）转换为JavaScript数组。
        ///默认情况下，如果无法转换任何类型，则会发生异常。
        /// </summary>
        /// <param name="enumerable">可枚举的对象，可转换为本地V8数组</param>
        /// <returns>本机V8数组</returns>
        public InternalHandle CreateValue(IEnumerable enumerable, bool ignoreErrors = false)
        {
            var values = (enumerable).Cast<object>().ToArray();
            InternalHandle[] handles = new InternalHandle[values.Length];
            for (var i = 0; i < values.Length; i++)
                try { handles[i] = CreateValue(values[i]); }
                catch (Exception ex) { if (!ignoreErrors) throw ex; }
            return CreateArray(handles);
        }

        /// <summary>
        /// 调用本地V8代理库以创建值实例，以在V8 JavaScript环境中使用。
        /// <para>此重载提供了一种'快速方法'来构造字符串数组。
        ///创建了一个大内存块来一次封送给定的字符串，这比必须创建单个本机字符串数组要快很多倍。</ para>
        /// </summary>
        public InternalHandle CreateValue(IEnumerable<string> items)
        {
            if (items == null) return V8NetProxy.CreateArray(_NativeV8EngineProxy, null, 0);

            var itemsEnum = items.GetEnumerator();
            int strBufSize = 0; // （存储块的字符串字符部分所需的大小）
            int itemsCount = 0;

            while (itemsEnum.MoveNext())
            {
                // 汇总所有字符串的长度
                strBufSize += itemsEnum.Current.Length + 1; // （+1为空字符）
                itemsCount++;
            }

            itemsEnum.Reset();

            int strPtrBufSize = Marshal.SizeOf(typeof(IntPtr)) * itemsCount; //开始缓冲区的大小以及所有字符串指针所需的大小。
            char** oneBigStringBlock = (char**)Utilities.AllocNativeMemory(strPtrBufSize + Marshal.SystemDefaultCharSize * strBufSize);
            char** ptrWritePtr = oneBigStringBlock;
            char* strWritePtr = (char*)(((byte*)oneBigStringBlock) + strPtrBufSize);
            int itemLength;

            while (itemsEnum.MoveNext())
            {
                itemLength = itemsEnum.Current.Length;
                Marshal.Copy(itemsEnum.Current.ToCharArray(), 0, (IntPtr)strWritePtr, itemLength);
                Marshal.WriteInt16((IntPtr)(strWritePtr + itemLength), 0);
                Marshal.WriteIntPtr((IntPtr)ptrWritePtr++, (IntPtr)strWritePtr);
                strWritePtr += itemLength + 1;
            }

            InternalHandle handle = V8NetProxy.CreateStringArray(_NativeV8EngineProxy, oneBigStringBlock, itemsCount);

            Utilities.FreeNativeMemory((IntPtr)oneBigStringBlock);

            return handle;
        }

        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// 只需创建并返回'空'JavaScript值即可
        /// </summary>
        public InternalHandle CreateNullValue() { return V8NetProxy.CreateNullValue(_NativeV8EngineProxy); }

        // --------------------------------------------------------------------------------------------------------------------

        /// <summary>
        /// 创建一个最能代表给定托管值的原生V8 JavaScript值。
        ///对象实例值将绑定到'V8NativeObject'包装器并返回。
        ///要包含对象类型的对象类型字段和属性的隐式包装，请将'recursive'设置为true，否则将跳过它们。
        /// <para>警告：整数是32位，数字（双精度）是64位。这意味着转换64位整数可能会导致数据丢失。</ para>
        /// </ summary>
        /// <param name="value">支持的值类型之一：bool，byte，Int16-64，Single，float，double，string，char，StringBuilder，DateTime或TimeSpan。 （警告：Int64将转换为Int32 [可能丢失数据]）</ param>
        /// <param name="recursive">对于对象实例，如果为true，则包括嵌套对象，否则仅绑定并返回对象本身。
        ///出于安全原因，指向对象实例的公共成员将被忽略。包括这些人也必须如此，有效地允许
        ///对象引用树的脚本内遍历（因此请确保它不会暴露敏感的方法/属性/字段）。</ param>
        /// <param name="memberSecurity">对于对象实例，这些是默认标志，用于描述所有对象实例成员的JavaScript属性，这些成员
        ///没有任何'ScriptMember'属性。标记应根据需要“或”在一起。</ param>
        /// <returns>最能代表给定管理值的本机值。</ returns>
        public InternalHandle CreateValue(object value, bool? recursive = null, ScriptMemberSecurity? memberSecurity = null)
        {
            if (value == null)
                return CreateNullValue();
            else if (value is IHandleBased)
                return ((IHandleBased)value).AsInternalHandle; // （已经是V8.NET值！）
            else if (value is bool)
                return CreateValue((bool)value);
            else if (value is byte)
                return CreateValue((Int32)(byte)value);
            else if (value is sbyte)
                return CreateValue((Int32)(sbyte)value);
            else if (value is Int16)
                return CreateValue((Int32)(Int16)value);
            else if (value is UInt16)
                return CreateValue((Int32)(UInt16)value);
            else if (value is Int32)
                return CreateValue((Int32)value);
            else if (value is UInt32)
                return CreateValue((double)(UInt32)value);
            else if (value is Int64)
                return CreateValue((double)(Int64)value); // （警告：转换64int-> 64float时可能会发生数据丢失）
            else if (value is UInt64)
                return CreateValue((double)(UInt64)value); // （警告：转换64int-> 64float时可能会发生数据丢失）
            else if (value is Single)
                return CreateValue((double)(Single)value);
            else if (value is float)
                return CreateValue((double)(float)value);
            else if (value is double)
                return CreateValue((double)value);
            else if (value is string)
                return CreateValue((string)value);
            else if (value is char)
                return CreateValue(((char)value).ToString());
            else if (value is StringBuilder)
                return CreateValue(((StringBuilder)value).ToString());
            else if (value is DateTime)
                return CreateValue((DateTime)value);
            else if (value is TimeSpan)
                return CreateValue((TimeSpan)value);
            else if (value is Enum) // （枚举只是整数之类的值）
                return CreateValue((int)value);
            else if (value is Array)
                return CreateValue((IEnumerable)value);
            else //??if (value.GetType().IsClass)
                return CreateBinding(value, null, recursive, memberSecurity);

            //??var type = value != null ? value.GetType().Name : "null";
            //??throw new NotSupportedException("Cannot convert object of type '" + type + "' to a JavaScript value.");
        }

        // --------------------------------------------------------------------------------------------------------------------
    }

    // ========================================================================================================================
}
